/*
 * @Author: hongbin
 * @Date: 2022-06-24 08:03:00
 * @LastEditors: hongbin
 * @LastEditTime: 2022-09-17 23:35:25
 * @Description:自己实现一个第一人称视角控制器
 */

import THREE from "./";

const { Euler, Vector3 } = THREE;

const getPointerLockElement = () =>
    document.pointerLockElement ||
    //@ts-ignore
    document.mozPointerLockElement ||
    //@ts-ignore
    document.webkitPointerLockElement;

const requestPointerLock = (element: HTMLElement) =>
    element.requestPointerLock ||
    //@ts-ignore
    element.mozRequestPointerLock ||
    //@ts-ignore
    element.webkitRequestPointerLock;

// camera: THREE.PerspectiveCamera,
// wrap: THREE.Object3D,
const { PI } = Math;
function HControls(wrap: THREE.Object3D, element: HTMLElement, acterWrap: THREE.Object3D) {
    const controls = {
        _isLocked: false,
        _syncRotate: false,
        _moving: false,
    } as any;
    let lockCallback = () => {};
    let unlockCallback = () => {};
    let camera: THREE.PerspectiveCamera;
    const _euler = new Euler(0, 0, 0, "YXZ");
    const _vector = new Vector3();
    const _PI_2 = PI / 2;
    const polarAngle = {
        min: -PI,
        max: PI,
        changePolarAngle: (angle: { min: number; max: number }) => {
            Object.assign(polarAngle, angle);
        },
    };

    let moveTimer: NodeJS.Timeout;

    Object.defineProperties(controls, {
        isLocked: {
            get: function () {
                return this._isLocked;
            },
            set: function (v: boolean) {
                this._isLocked = v;
            },
        },
        syncRotate: {
            get: () => {
                return controls._syncRotate;
            },
            set: (v: boolean) => {
                controls._syncRotate = v;
            },
        },
        _moving: {
            get: () => {
                return controls.__moving;
            },
            set: (v: boolean) => {
                controls.__moving = v;
            },
        },
    });

    // const minPolarAngle = 0; // 最小旋转弧度
    // const maxPolarAngle = Math.PI;
    const pointerSpeed = 1.0; //速度 后续shift 加速

    init();

    function init() {
        element.requestPointerLock = requestPointerLock(element);
        document.addEventListener("pointerlockchange", pointerLockChange, false);
        document.addEventListener("mozpointerlockchange", pointerLockChange, false);
        document.addEventListener("webkitpointerlockchange", pointerLockChange, false);
    }

    function move(e: MouseEvent | any) {
        // 兼容每次触发的增量值
        const movementX = e.movementX || e.mozMovementX || e.webkitMovementX || 0;
        const movementY = e.movementY || e.mozMovementY || e.webkitMovementY || 0;

        // camera.rotation.set(x, y, 0); 不能这样简单设置

        _euler.setFromQuaternion(wrap.quaternion);

        _euler.y -= movementX * 0.002 * pointerSpeed;
        _euler.x -= movementY * 0.002 * pointerSpeed;

        //限制头能转多少度 180
        // _euler.x = Math.max(-_PI_2, Math.min(_PI_2, _euler.x));
        _euler.x = Math.max(_PI_2 - polarAngle.max, Math.min(_PI_2 - polarAngle.min, _euler.x));

        wrap.quaternion.setFromEuler(_euler);

        //判断鼠标当前是否正在在移动
        if (!controls.moving) {
            controls.moving = true;
            clearTimeout(moveTimer);
            moveTimer = setTimeout(() => {
                controls.moving = false;
            }, 20);
        }

        {
            // const maxPolarAngle = Math.PI / 2;
            // const minPolarAngle = Math.PI / 2;
            // _euler.x = Math.max(
            //     Math.PI / 2 - maxPolarAngle,
            //     Math.min(Math.PI / 2 - minPolarAngle, _euler.x)
            // );
            // acterWrap.quaternion.setFromEuler(_euler);
        }
    }

    function keyPressMove(distance: number, vertical: boolean) {
        //镜头没移动的时候按键盘 跟着人物朝向走  移动镜头时 按移动键 跟着镜头朝向走
        const target = !controls.moving ? acterWrap : wrap;
        //鼠标没移动的情况 随着人物朝向移动 而不是摄像头看的方向
        // if (!controls.moving) {
        //     _vector.setFromMatrixColumn(acterWrap.matrix, 0);
        //     vertical && _vector.crossVectors(acterWrap.up, _vector);
        // } else {
        //     _vector.setFromMatrixColumn(wrap.matrix, 0);
        //     vertical && _vector.crossVectors(wrap.up, _vector);
        // }
        _vector.setFromMatrixColumn(target.matrix, 0);
        vertical && _vector.crossVectors(target.up, _vector);
        wrap.position.addScaledVector(_vector, distance);
        acterWrap.position.addScaledVector(_vector, distance);

        //移动时跟随摄像头旋转
        if (controls.syncRotate) {
            const maxPolarAngle = Math.PI / 2;
            const minPolarAngle = Math.PI / 2;
            _euler.x = Math.max(Math.PI / 2 - maxPolarAngle, Math.min(Math.PI / 2 - minPolarAngle, _euler.x));
            acterWrap.quaternion.setFromEuler(_euler);
        }
    }

    function moveForward(distance: number) {
        // move forward parallel to the xz-plane
        // assumes camera.up is y-up
        keyPressMove(distance, true);
    }

    function moveRight(distance: number) {
        keyPressMove(distance, false);
    }

    function handleWheel(distance: number) {
        if (!camera) {
            camera = wrap.getObjectByName("camera") as THREE.PerspectiveCamera;
            throw new Error("没有获取到camera");
        }
        //按照滚动次数计算 一次移动1个单位
        const direction = distance > 0 ? -1 : 1;
        const z = camera.position.z + direction;
        if (z >= 13 && z <= 100) camera.position.z += direction;
    }

    function wheelListen(e: { detail: number }) {
        handleWheel(e.detail);
    }

    function mousewhill(e: { wheelDelta: number }) {
        handleWheel(e.wheelDelta);
    }

    function pointerLockChange() {
        controls.isLocked = !!getPointerLockElement();

        if (controls.isLocked) {
            console.log("锁定");
            lockCallback && lockCallback();
            //添加监听
            document.addEventListener("mousemove", move);
            camera = wrap.getObjectByName("camera") as THREE.PerspectiveCamera;
            /**
             *监听鼠标滚轮
             */
            window.onmousewheel = document.onmousewheel = mousewhill;

            /**
             * 兼容firefox浏览器鼠标滚轮监听
             */
            document.addEventListener("DOMMouseScroll", wheelListen as any);
        } else {
            console.log("退出锁定");
            unlockCallback && unlockCallback();
            //移除监听
            document.removeEventListener("mousemove", move);
            window.onmousewheel = document.onmousewheel = null;
            document.removeEventListener("DOMMouseScroll", wheelListen as any);
        }
    }

    function lock() {
        //@ts-ignore
        !controls.isLocked && element.requestPointerLock();
    }

    const direction = new Vector3(0, 0, -1);
    function getDirection(v: THREE.Vector3) {
        //@ts-ignore
        return v.copy(direction).applyQuaternion(acterWrap.quaternion);
        // return v.copy(direction).applyQuaternion();
    }

    function getObject() {
        //@ts-ignore
        return acterWrap;
    }

    const addEventListener = (type: "lock" | "unlock", callback: () => void) => {
        if (type === "lock") lockCallback = callback;
        if (type === "unlock") unlockCallback = callback;
    };

    Object.assign(controls, {
        lock,
        addEventListener,
        moveForward,
        moveRight,
        getDirection,
        getObject,
        changePolarAngle: polarAngle.changePolarAngle,
    });

    return controls;
}

export default HControls;
